package org.jeecg.common.util;

import com.github.junrar.Archive;
import com.github.junrar.rarfile.FileHeader;
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream;
import org.jeecg.common.exception.JeecgBootException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.nio.charset.Charset;
import java.util.Enumeration;
import java.util.UUID;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

/**
 * 文件解压工具
 */
public class CompressedFileUtil {

    private final static Logger lg = LoggerFactory.getLogger(CompressedFileUtil.class);

    private static final String FILE_SUFFIX_RAR = ".rar";
    private static final String FILE_SUFFIX_ZIP = ".zip";
    private static final String FILE_SUFFIX_TGZ = ".tgz";

    /**
     * 是否压缩文件
     * @param fileName
     * @return
     */
    public static boolean isCPSFile(String fileName){
        return fileName.endsWith(FILE_SUFFIX_RAR) || fileName.endsWith(FILE_SUFFIX_ZIP) || fileName.endsWith(FILE_SUFFIX_TGZ);
    }

    /**
     * 解压文件
     * @param fileName
     * @param inputStream
     * @return
     */
    public static String unFile(String fileName, InputStream inputStream) {
        String targetPath = System.getProperty("java.io.tmpdir") + "/cosmos-data/unfile";
        File tempFile = new File(targetPath);
        if(!tempFile.exists()){
            tempFile.mkdirs();
        }
        try{
            targetPath = targetPath + "/" + UUID.randomUUID().toString();
            if (fileName.toLowerCase().endsWith(FILE_SUFFIX_RAR)) {
                unRAR(inputStream, targetPath);
            } else if(fileName.toLowerCase().endsWith(FILE_SUFFIX_ZIP)) {
                unZIP(inputStream, targetPath);
            } else if(fileName.toLowerCase().endsWith(FILE_SUFFIX_TGZ)) {
                unTGZ(inputStream, targetPath);
            } else {
                throw new JeecgBootException("无法解压文件");
            }
        } catch (Exception e){
            // 删除解压目录
            FileUtil.delDir(targetPath);
            throw new JeecgBootException(e);
        }
        return targetPath;
    }

    /**
     * 尝试设置编码获取ZipFile对象
     * @param filePath
     * @return
     * @throws Exception
     */
    private static ZipFile getZipFile(String filePath) throws Exception{
        try{
            return getZipFile(filePath, "GBK");
        } catch (IllegalArgumentException e){
            try{
                return getZipFile(filePath, "UTF-8");
            } catch (IllegalArgumentException e2){
                return getZipFile(filePath, "GB2312");
            }
        }
    }

    /**
     * 根据编码获取ZipFile对象
     * @param filePath
     * @param charset
     * @return
     * @throws IOException
     */
    private static ZipFile getZipFile(String filePath, String charset) throws IOException {
        try(ZipFile zFile = new ZipFile(filePath, Charset.forName(charset));){
            Enumeration<?> enu = zFile.entries();
            if(enu.hasMoreElements()){
                enu.nextElement();
            }
        }
        lg.info("压缩包文件文件编码:" + filePath + "#" + charset);
        return new ZipFile(filePath, Charset.forName(charset));
    }

    /**
     * 保存
     *
     * @param inputStream
     * @param outDir
     * @throws Exception
     */
    private static void unZIP(InputStream inputStream, String outDir) throws Exception {
        // 写入临时目录
        String tempPath = System.getProperty("java.io.tmpdir") + "/cosmos-data/temp";
        File tempFile = new File(tempPath);
        if(!tempFile.exists()){
            tempFile.mkdirs();
        }

        String filePath = null;
        try{
            filePath = tempPath + "/" + UUID.randomUUID().toString() + FILE_SUFFIX_ZIP;
            FileUtil.writeToLocal(filePath, inputStream);

            lg.info("开始解压文件：" + filePath);
            try (ZipFile zFile = getZipFile(filePath);){
                // 得到压缩文件实例
                ZipEntry entry;
                // 得到压缩条目的枚举对象
                Enumeration<?> enu = zFile.entries();
                // 依次枚举条目
                while (enu.hasMoreElements()) {
                    // 得到压缩条目
                    entry = (ZipEntry) enu.nextElement();
                    if (entry.isDirectory()) {
                        new File(outDir, entry.getName()).mkdirs();
                        continue;
                    }

                    // 创建文件
                    File outputFile = new File(outDir, entry.getName());
                    try (
                         InputStream itemIS = zFile.getInputStream(entry);
                         OutputStream outputStream = new FileOutputStream(outputFile)) {
                        byte[] buffer = new byte[1024];
                        int length;
                        while ((length = itemIS.read(buffer)) != -1) {
                            outputStream.write(buffer, 0, length);
                        }
                    }
                }
            }
            lg.info("文件解压成功：" + filePath);
        } finally {
            new File(filePath).delete();
        }
    }

    /**
     * rar文件解压
     *
     * @param inputStream
     * @param outDir
     * @throws Exception
     */
    private static void unRAR(InputStream inputStream, String outDir) throws Exception {
        File out = null;
        try (Archive archive = new Archive(inputStream);){
            FileHeader fileHeader = archive.nextFileHeader();
            while (fileHeader != null) {
                // 处理中文乱码问题
                String fileName = !fileHeader.isUnicode() ? fileHeader.getFileNameString() : fileHeader.getFileNameW();
                lg.info("fileName:" + fileName);
                String fileNameString = fileName.replace("\\", "/");
                out = new File(outDir + fileNameString);
                if (fileHeader.isDirectory()) {
                    fileHeader = archive.nextFileHeader();
                    if (!out.exists()) {
                        out.mkdirs();
                        lg.info("rarout:" + out);
                    }
                    continue;
                }
                lg.info("rarout:" + out);
                if (!out.exists()) {
                    if (!out.getParentFile().exists()) {
                        out.getParentFile().mkdirs();
                        lg.info("rarout:" + out.getParentFile());
                    }
                    out.createNewFile();
                }

                try (FileOutputStream os = new FileOutputStream(out);){
                    archive.extractFile(fileHeader, os);
                }
                fileHeader = archive.nextFileHeader();
            }
        }
    }

    /**
     * 解压TGZ文件
     * @param inputStream
     * @param outputDirectory
     * @throws IOException
     */
    private static void unTGZ(InputStream inputStream, String outputDirectory) throws IOException {
         try (TarArchiveInputStream tarInputStream = new TarArchiveInputStream(new GzipCompressorInputStream(inputStream))) {
              TarArchiveEntry entry;
              while ((entry = tarInputStream.getNextTarEntry()) != null) {
                 if (entry.isDirectory()) {
                     // 创建目录
                     new File(outputDirectory, entry.getName()).mkdirs();
                     continue;
                 }
                 // 创建文件
                 File outputFile = new File(outputDirectory, entry.getName());
                 try (OutputStream outputStream = new FileOutputStream(outputFile)) {
                     byte[] buffer = new byte[1024];
                     int length;
                     while ((length = tarInputStream.read(buffer)) != -1) {
                         outputStream.write(buffer, 0, length);
                     }
                 }
              }
         }
    }
}
